# -*- coding: utf-8 -*-
# @Time : 2022-01-17 11:03
# @Author : Z
# @Function :
import base64
import json
import re
import time
import traceback

import requests
import urllib3
from jsonpath import jsonpath

from Common.Logger import logger, path
from Common.mysqltest import MysqlTest
from Common.ver.verify import Verify


class Inter:

    # 初始化
    def __init__(self):
        self.session = requests.session()
        self.url = None
        self.text_result = ''
        self.json_result = {}
        self.soap_result = {}
        self.relations = {}
        self.verify = Verify('zengwenxiang', '7812977zwx', '927466')
        # 取消ssl警告
        self.urllib3 = urllib3.disable_warnings()

    # 设置url
    def set_url(self, url: str = ''):
        self.url = url
        logger.info(self.url)
        return self.url

    # 关联参数,保存到全局字典relations中
    def __get_relations(self, params: str):
        if params:
            for key in self.relations.keys():
                # params.replace(旧值，新值)   新值替换params的旧值，若旧值不存在params中，则保持原来数据
                params = params.replace('{' + key + '}', self.relations.get(key))
            for key in self.json_result.keys():
                # 将上一步请求的结果也进行替换
                params = params.replace('{' + key + '}', str(self.json_result.get(key)))
            return params
        else:
            return params

    # 参数处理成字典
    def __get_data(self, params: str):
        if params.__contains__('=') and not params.startswith('{'):
            params_dict = {}
            # 原始数据 app=weather.today&weaid=1&appkey=10003
            # 通过 & 分割数据，分割后的数据为list：[app=weather.today', 'weaid=1', 'appkey=10003']
            params = params.split('&')
            # 遍历list，通过键和值，组成字典
            for key_value in params:
                # 通过find() 查找指定的字符串并返回该字符串的起始位置，
                # [:结束位置]  例子：a="0123456"  b=[:1]  b="0", 结束的下标值取不到，就是1下标值的取不到
                key = key_value[0:key_value.find('=')]
                # [起始位置+1:]  例子：a="0123456"  b=[1:]  b="123456"，结束下标不指定表示能够获取到最后下标的数据
                value = key_value[key_value.find('=') + 1:]
                params_dict[key] = value
            return params_dict
        elif params.startswith('{') or params.startswith("[") :
            # 如果直接传入json字符串字典或者列表，通过json.loads转换成字典或列表
            params = json.loads(params)
            return params
        else:
            return None

    # 添加头域
    def add_header(self, key: str = '', value: str = ''):
        value = self.__get_relations(value)
        self.session.headers[key] = value
        logger.info("添加header参数：" + key + ':' + value)
        return self.session.headers

    # 添加多个头域
    def add_headers(self, params: str = ''):
        # 先关联，后处理成字典，最后遍历字典，添加到头域
        value = self.__get_relations(params)
        logger.info(value)
        value = self.__get_data(value)
        # logger.info(value)
        for key in value:
            self.session.headers[key] = value.get(key)
            logger.info("添加header参数：" + key + ':' + value.get(key))
        return self.session.headers

    # 移除头域
    def remove_header(self, key: str = ''):
        try:
            self.session.headers.pop(key)
            logger.info("移除header参数：" + key)
        except:
            pass
            logger.info("无header参数：" + key)
        return self.session.headers

    # get请求的参数，可以传字典，但是需要用params =，也可以将参数加入到url中
    def get(self, name: str = ''):
        if not name.startswith('http'):
            name = self.url +  name
        name = self.__get_relations(name)
        try:
            result = self.session.get(name)
            self.text_result = result.text
            try:
                self.json_result = json.loads(self.text_result)
                logger.info(name + " 接口返回：" + str(self.json_result))
                return self.json_result
            except Exception as e:
                self.json_result = {'error': 'json处理异常'}
                logger.error(name + " 接口返回：" + str(self.json_result))
                return self.json_result
        except Exception as e:
            self.text_result = str(traceback.format_exc())
            self.json_result = {'error': '请求异常'}
            logger.error(name + " 接口返回：" + str(self.text_result))
            return self.text_result

    # post 请求   data:传递表单类型的参数（参数类型为：Content-Type:application/x-www-form-urlencoded）
    def post_data(self, name: str = '', params: str = ''):
        if not name.startswith('http'):
            name = self.url  + name
        name=self.__get_relations(name)
        if params:
            params = self.__get_relations(params)
            params = self.__get_data(params)
            # logger.info(params)
        try:
            # 指定内容类型，请求完后，再移除请求类型，不会对下一次请求有影响
            self.session.headers['Content-Type'] = 'application/x-www-form-urlencoded'
            result = self.session.post(name, data=params, verify=False)
            # 如果需要编码处理
            # self.text_result=result.content.decode(('utf-8'))
            self.text_result = result.text
            self.session.headers.pop('Content-Type')
            try:
                self.json_result = json.loads(self.text_result)
                logger.info(name + " 接口返回：" + str(self.json_result))
                return self.json_result
            except Exception as e:
                self.json_result = {'error': 'json处理异常'}
                logger.error(name + " 接口返回：" + str(self.json_result))
                logger.error(result.text)
                return traceback.format_exc()
        except Exception as e:
            self.text_result = str(traceback.format_exc())
            self.json_result = {'error': '请求异常'}
            logger.error(name + " 接口返回：" + str(self.text_result))
            return self.text_result

    # post请求  data:json参数（参数类型为：Content-Type:application/json）
    def post_json(self, name: str = '', params=None):
        if not name.startswith('http'):
            name = self.url + name
        if params:
            # logger.info(params)
            params = self.__get_relations(params)
            # logger.info(params)
            params = self.__get_data(params)
            # logger.info(params)
        try:
            self.session.headers['Content-Type'] = 'application/json'
            result = self.session.post(name, json=params, verify=False)
            self.session.headers.pop('Content-Type')
            # self.text_result = result.content.decode(('utf-8'))
            self.text_result = result.text
            try:
                self.json_result = json.loads(self.text_result)
                logger.info(name + " 接口返回：" + str(self.json_result))
                return self.json_result
            except Exception as e:
                self.json_result = {'error': 'json处理异常'}
                logger.error(name + " 接口返回：" + str(self.json_result))
                return self.json_result
        except Exception as e:
            self.text_result = str(traceback.format_exc())
            self.json_result = {'error': '请求异常'}
            logger.error(name + " 接口返回：" + str(self.text_result))
            return self.text_result

    # post请求  files: 用于上传文件（参数类型： content-type:multipart/form-data;）混合表单类型
    def post_upload(self, name: str = '', filename: str = '', file_type: str = '', params: str = ''):
        if not name.startswith('http'):
            name = self.url + '/' + name
        if params:
            params = self.__get_relations(params)
            params = self.__get_data(params)
        # open函数打开文件并将文件对象存在字典中，文件名称，文件路径，文件类型
        files = {'file': (filename, open(path + '/lib/imgs/' + filename, 'rb'), file_type)}
        try:
            # 是不需要再指定传入的内容类型
            # self.session.headers['Content-Type'] = 'multipart/form-data'
            # files传入待上传文件参数files,data传入请求参数params
            result = self.session.post(url=name, data=params, files=files)
            self.text_result = result.text
            try:
                # 将返回结果处理成字典格式
                self.json_result = json.loads(self.text_result)
                logger.info(name + " 接口返回：" + str(self.json_result))
                return self.json_result
            except Exception as e:
                logger.error('json处理异常')
                self.json_result = {'error': 'json处理异常'}
                return self.json_result
        except Exception as e:
            self.text_result = str(traceback.format_exc())
            self.json_result = {'error': '请求异常'}
            logger.error('请求异常，' +self.text_result)
            return self.text_result

    # post的soap格式请求
    def post_soap(self, url: str = '', param: str = ''):
        if not url.startswith('http'):
            url = self.url + '/' + url
        try:
            self.soap_result = self.session.post(url=url, data=param).text
            logger.info(self.soap_result)
            return self.soap_result
        except Exception as e:
            self.soap_result = {'error': 'post请求异常'}
            logger.error(self.soap_result)
            return self.soap_result

    # 将上一步的请求结果的一个字段保存为全局变量
    def save_json(self, json_key: str = '', param_name: str = ''):
        try:
            # 通过jsonpath()提取
            json_value = str(jsonpath(self.json_result, json_key)[0])
        except:
            json_value = 'None'
        self.relations[param_name] = json_value
        logger.info(param_name + ":" + json_value)
        return self.relations

    def save_param(self, key_name: str = '', param_name: str = ''):
        key_name=self.__get_relations(key_name)
        param_name=self.__get_relations(param_name)
        self.relations[key_name]=param_name
        return self.relations


    # 断言结果是否和预期一致
    def assert_equals(self, result_key: str = '', expect_result: str = ''):
        try:
            actual_result = str(jsonpath(self.json_result, result_key)[0])
        except Exception as e:
            actual_result = None
        expect_result = self.__get_relations(expect_result)
        if actual_result == expect_result:
            logger.info('断言成功,实际结果与预期结果一致，' + '实际:' + str(actual_result) + "，预期:" + str(expect_result))
            return 1, actual_result
        else:
            logger.error('断言失败,实际结果与预期结果不一致，' + '实际:' + str(actual_result) + "，预期:" + str(expect_result))
            return 0, actual_result

    # 正则提取实际结果，进行post_soap请求的断言
    def assertequalsRe(self, expression: str = '', expect_result: str = ''):
        try:
            actual_result = re.findall(expression, str(self.soap_result))
            actual_result = actual_result[0]
        except Exception as e:
            logger.error("请检查正则表达式是否正确" + expression)
            actual_result = None
        expect_result = self.__get_relations(expect_result)
        if actual_result == expect_result:
            logger.info('断言成功,实际结果与预期结果一致，' + '实际:' + actual_result + " 预期:" + expect_result)
            return 1, actual_result
        else:
            logger.error('断言成功,实际结果与预期结果不一致，' + '实际:' + str(actual_result) + " 预期:" + str(expect_result))
            return 0, actual_result

    # 查询数据库数据，进行断言
    def assertmysql(self, sql: str = '',  expected: str = '',param: str = ''):
        try:
            expected = self.__get_relations(expected)
            # print("预期结果："+expected)
            mytest = MysqlTest()
            # 获取数据库查询结果
            result = mytest.mysql_execute(sql)
            # print("获取数据库执行sql语句的结果："+str(result))
            # 数据库查询到数据不为空
            if result:
                actual = result[0].get(param)
                if actual.__contains__('.'):
                    actual=actual[:actual.find('.')+2]
                # print("获取数据库获取字段"+param+"的结果："+str(actual))
                if str(actual) == expected:
                    logger.info('断言成功,实际结果与预期结果一致，' + '实际:' + actual + " 预期:" + expected)
                    return 1, actual
                else:
                    logger.error('断言失败,实际结果与预期结果不一致，' + '实际:' + actual + " 预期:" + expected)
                    return 0, actual
            # 数据库查询结果为空
            else:
                actual=str(result)
                # print("获取数据库获取字段"+param+"的结果："+str(actual))
                if actual == expected:
                    logger.info('断言成功,实际结果与预期结果一致，' + '实际:' + actual + " 预期:" + expected)
                    return 1, actual
                else:
                    logger.error('断言失败,实际结果与预期结果不一致，' + '实际:' + actual + " 预期:" + expected)
                    return 0, actual
        except:
            logger.error("数据库查询出错，测试失败" + traceback.format_exc())
            return traceback.format_exc()

    # 格式化当前时间,"%Y-%m-%d %H:%M:%S"
    def now_time(self, format: str = ''):
        now_time = time.strftime(format, time.localtime())
        self.relations['now_time'] = now_time
        logger.info(now_time)
        return now_time

    def reauth(self):
        self.post_data('logout')
        self.add_header('token')
        self.post_data('auth')
        self.save_json('token', 'token')
        self.add_header('token', '{token}')

    # 提取图形验证码
    def get_verify(self, param_key: str = '', save_key: str = ''):
        try:
            # 关联参数,提取base64的数据
            base64_code = self.__get_relations(param_key)
            # 再从data中获取base64的编码数据
            # base64_code = self.relations[param_key]
            # 截取base64的编码数据
            base64_code = base64_code[22:]
            f = open(path + '/lib/verify/base64_code.gif', mode="wb")
            f.write(base64.b64decode(base64_code))
            f.close()
            verify = self.verify.PostPic(6001, im='../../lib/verify/base64_code.gif')
            self.relations[save_key] = verify
            logger.info("图形验证码：" + verify)
            return verify
        except:
            logger.error("图形验证码获取失败" + traceback.format_exc())
            return traceback.format_exc()

    # 从数据库提取相关字段，并保存到字典中
    def save_sql(self,sql: str = '',param: str = ''):
        try:
            mytest = MysqlTest()
            sql = self.__get_relations(sql)
            param = self.__get_relations(param)
            res1 = mytest.mysql_execute(sql)
            res2 = res1[0].get(param)
            self.relations[param] = res2
            logger.info(param+":"+str(res2))
            return self.relations
        except:
            self.relations[param] = "None"
            logger.error("数据库查询失败" + traceback.format_exc())
            return traceback.format_exc()
